#ifndef __U2Camera_H__
#define __U2Camera_H__


#include "U2PreRequest.h"
#include "U2MemoryAllocatorConfig.h"
#include "U2Vector2.h"
#include "U2Matrix4.h"
#include "U2Frustum.h"


U2EG_NAMESPACE_BEGIN


class U2Viewport;
class U2SceneManager;


class _U2Share U2Camera : public U2Frustum
{
public:
    /** Listener interface so you can be notified of Camera events. 
    */
    class _U2Share Listener 
    {
    public:
	    Listener() {}
	    virtual ~Listener() {}

	    /// Called prior to the scene being rendered with this camera
	    virtual void cameraPreRenderScene(U2Camera* cam)
                    { (void)cam; }

	    /// Called after the scene has been rendered with this camera
	    virtual void cameraPostRenderScene(U2Camera* cam)
                    { (void)cam; }

	    /// Called when the camera is being destroyed
	    virtual void cameraDestroyed(U2Camera* cam)
                    { (void)cam; }
    };

protected:
    
    /// Camera position - default (0,0,0)
    U2Vector3       mPosition;
    /// Derived orientation/position of the camera, including reflection
    U2Vector3       mDerivedPosition;
    /// u2real world orientation/position of the camera
    U2Vector3       mRealPosition;
    /// The last viewport to be added using this camera
    U2Viewport*     mLastViewport;
    /** Whether aspect ratio will automatically be recalculated 
        when a viewport changes its size.
    */
    bool            mAutoAspectRatio;
    /// Stored number of visible faces in the last render
    unsigned int    mVisFacesLastRender;
    /// Stored number of visible faces in the last render
    unsigned int    mVisBatchesLastRender;
    /// @see Camera::getPixelDisplayRatio
    u2real          mPixelDisplayRatio;
    /// Rendering type
    PolygonMode     mSceneDetail;

    typedef std::vector<Listener*>      ListenerList;
    ListenerList    mListeners;

    /// Scene manager responsible for the scene
    U2SceneManager* mSceneMgr;


public:
    /** Standard constructor. */
    U2Camera(const U2String& name, U2SceneManager* sm);

    /** Standard destructor. */
    virtual ~U2Camera();

    /** Sets the camera's position. */
    void setPosition(u2real x, u2real y, u2real z);

    /** Sets the camera's position. */
    void setPosition(const U2Vector3& vec);

    /** Retrieves the camera's position. */
    const U2Vector3& getPosition(void) const;

    /** Get the last viewport which was attached to this camera. 
    @note
        This is not guaranteed to be the only viewport which is
        using this camera, just the last once which was created referring
        to it.
    */
    U2Viewport* getViewport(void) const { return mLastViewport; }
    /** Notifies this camera that a viewport is using it. */
    void _notifyViewport(U2Viewport* viewport) { mLastViewport = viewport; }

    /** If set to true a viewport that owns this frustum will be able to 
        recalculate the aspect ratio whenever the frustum is resized.
    @remarks
        You should set this to true only if the frustum / camera is used by 
        one viewport at the same time. Otherwise the aspect ratio for other 
        viewports may be wrong.
    */    
    void setAutoAspectRatio(bool autoratio);
    /** Retrieves if AutoAspectRatio is currently set or not
    */
    bool getAutoAspectRatio(void) const;

    /** Internal method to notify camera of the visible faces in the last render.
    */
    void _notifyRenderedFaces(unsigned int numfaces);

    /** Internal method to notify camera of the visible batches in the last render.
    */
    void _notifyRenderedBatches(unsigned int numbatches);

    /** Internal method to retrieve the number of visible faces in the last render.
    */
    unsigned int _getNumRenderedFaces(void) const;

    /** Internal method to retrieve the number of visible batches in the last render.
    */
    unsigned int _getNumRenderedBatches(void) const;

    /** Sets the level of rendering detail required from this camera.
    @remarks
        Each camera is set to render at full detail by default, that is
        with full texturing, lighting etc. This method lets you change
        that behaviour, allowing you to make the camera just render a
        wireframe view, for example.
    */
    void setPolygonMode(PolygonMode sd);

    /** Retrieves the level of detail that the camera will render.
    */
    PolygonMode getPolygonMode(void) const;


    /** Tells the Camera to contact the SceneManager to render from it's viewpoint.
    @param vp The viewport to render to
    @param includeOverlays Whether or not any overlay objects should be included
    */
    void _renderScene(U2Viewport *vp);

	/// @copydoc Frustum::getViewMatrix
	//const U2Matrix4& getViewMatrix(void) const;
	/** Specialised version of getViewMatrix allowing caller to differentiate
		whether the custom culling frustum should be allowed or not. 
	@remarks
		The default behaviour of the standard getViewMatrix is to delegate to 
		the alternate culling frustum, if it is set. This is expected when 
		performing CPU calculations, but the final rendering must be performed
		using the real view matrix in order to display the correct debug view.
	*/
	const U2Matrix4& getViewMatrix(bool ownFrustumOnly) const;

    /** Points the camera at a location in worldspace.
        @remarks
            This is a helper method to automatically generate the
            direction vector for the camera, based on it's current position
            and the supplied look-at point.
        @param
            targetPoint A vector specifying the look at point.
    */
    /**********************@@@@@@@@@@@@@@@@@@@@@
    void lookAt( const U2Vector3& targetPoint );
    */
    /** Points the camera at a location in worldspace.
        @remarks
            This is a helper method to automatically generate the
            direction vector for the camera, based on it's current position
            and the supplied look-at point.
        @param
            x
        @param
            y
        @param
            z Co-ordinates of the point to look at.
    */
    /**********************@@@@@@@@@@@@@@@@@@@@@
    void lookAt(u2real x, u2real y, u2real z);
    */

    /** Sets the camera's direction vector.
        @remarks
            Note that the 'up' vector for the camera will automatically be recalculated based on the
            current 'up' vector (i.e. the roll will remain the same).
    */
    /**********************@@@@@@@@@@@@@@@@@@@@@
    void setDirection(u2real x, u2real y, u2real z);
    */

    /** Sets the camera's direction vector.
    */
    /**********************@@@@@@@@@@@@@@@@@@@@@
    void setDirection(const U2Vector3& vec);
    */

    /* Gets the camera's direction.
    */
    /**********************@@@@@@@@@@@@@@@@@@@@@
    Vector3 getDirection(void) const;
    */
};


U2EG_NAMESPACE_END


#endif
